
package com.unism.common.jpa;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import java.util.logging.Level;

import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.LockModeType;
import javax.persistence.NonUniqueResultException;
import javax.persistence.Query;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.jpa.EntityManagerFactoryUtils;

/**
 * A data access object (DAO) providing persistence and search support for TEnum
 * entities. Transaction control of the save(), update() and delete() operations
 * can directly support Spring container-managed transactions or they can be
 * augmented to handle user-managed Spring transactions. Each of these methods
 * provides additional information for how to configure it for the desired type
 * of transaction control.
 * 
 * @see com.TEnum.TEnum
 * @author MyEclipse Persistence Tools
 */

public class BaseDAO<T> {
	private final Logger log = LoggerFactory.getLogger(BaseDAO.class);

	private static final String ENTITY_MANAGER_FACTORY = "entityManagerFactory";

	@Resource(name = ENTITY_MANAGER_FACTORY)
	protected EntityManagerFactory emf;

	private EntityManager em;

	protected EntityManager getEntityManager() {
		em = EntityManagerFactoryUtils.getTransactionalEntityManager(emf);
		return em;
	}

	/*
	 * protected EntityManager getEntityManager() { return
	 * EntityManagerFactoryUtils.getTransactionalEntityManager(emf); }
	 */

	/*
	 * protected EntityManager getEntityManager() { if (em == null) {
	 * synchronized (lock) { if (em == null) { emf = Persistence
	 * .createEntityManagerFactory(PERSISTENCE_UNIT_NAME); em =
	 * emf.createEntityManager(); } } } return em; }
	 */

	/**
	 * 生成主键ID
	 */
	public String genPkId() {

		UUID uuid = UUID.randomUUID();

		return uuid.toString();
	}

	/**
	 * 将传入的detached状态的对象的属性复制到持久化对象中，并返回该持久化对象
	 * 
	 * @param detachedInstance
	 * @return
	 */
	public T merge(T detachedInstance) {
		log.debug("merging T instance");
		try {
			T result = getEntityManager().merge(detachedInstance);
			log.debug("merge successful");
			return result;
		} catch (RuntimeException re) {
			log.error("merge failed", re);
			throw re;
		}
	}

	/**
	 * 将传入的对象状态设置为Transient状态
	 * 
	 * @param instance
	 */
	public void attachClean(T instance) {
		log.debug("attaching clean T instance");
		try {
			getEntityManager().lock(instance, LockModeType.NONE);
			log.debug("attach successful");
		} catch (RuntimeException re) {
			log.error("attach failed", re);
			throw re;
		}
	}

	public void saveOrUpdate(Query query, T entity) {
		final int NO_RESULT = 0;
		final int RESULT = 1;

		// should return a list of ONE result,
		// since the query should be finding unique objects
		List results = query.getResultList();
		switch (results.size()) {
		case NO_RESULT:
			getEntityManager().persist(entity);
		case RESULT:
			if (!getEntityManager().contains(entity)) {
				getEntityManager().merge(entity);
			}
		default:
			throw new NonUniqueResultException("Unexpected query results, " + results.size());
		}
	}

	/**
	 * 添加情报
	 * 
	 * <pre>
	 * 
	 * EntityManagerHelper.beginTransaction();
	 * AttentionDAO.save(entity);
	 * EntityManagerHelper.commit();
	 * </pre>
	 * 
	 * @param entity
	 *            操作表对应的实体
	 * @throws RuntimeException
	 *             操作失敗
	 */
	public void save(T entity) {
		getEntityManager().persist(entity);
	}

	public void save(T entity, boolean autoCommit) {
		log.debug("saving   instance", Level.INFO, null);
		try {
			if (autoCommit) {
				beginTrans();
			}
			getEntityManager().persist(entity);

			if (autoCommit) {
				commit();
			}
			log.debug("save successful", Level.INFO, null);
		} catch (RuntimeException re) {
			log.debug("save failed", Level.SEVERE, re);
			if (autoCommit) {
				rollback();
			}
			throw re;
		}
	}

	/**
	 * 批量保存实体集
	 * 
	 * @param entitys
	 * @return
	 */
	public boolean saveEntitys(List<T> entitys) {

		// 为了提升性能，我们对数据进行分批次的保存，手动控制更新到数据库
		boolean flag = false;
		try {

			/*
			 * 
			 * // 每100条往数据库里写入一次,相对提升性能，此值可改变 int batchSize = 100; int i = 0;
			 * for (T entity : entitys) { getEntityManager().persist(entity);
			 * i++; if (i % batchSize == 0) { getEntityManager().flush();
			 * getEntityManager().clear(); } }
			 */
			for (T entity : entitys) {
				getEntityManager().persist(entity);
			}
			getEntityManager().flush();
			getEntityManager().clear();
			flag = true;
			if (log.isDebugEnabled()) {
				log.debug("批量保存实体成功，" + getEntityManager().getClass().getName());
			}
		} catch (Exception e) {
			flag = false;
			log.error("批量保存实体失败", e);
		}
		return flag;
	}

	/**
	 * 批量更新实体集
	 * 
	 * @param entitys
	 * @return
	 */
	public boolean updateEntitys(List<T> entitys) {

		// 为了提升性能，我们对数据进行分批次的保存，手动控制更新到数据库
		boolean flag = false;
		try {
			for (T entity : entitys) {
				getEntityManager().merge(entity);
			}
			getEntityManager().flush();
			getEntityManager().clear();
			flag = true;
			if (log.isDebugEnabled()) {
				log.debug("批量保存实体成功，" + getEntityManager().getClass().getName());
			}
		} catch (Exception e) {
			flag = false;
			log.error("批量保存实体失败", e);
		}
		return flag;
	}

	public void beginTrans() {
		getEntityManager().getTransaction().begin();

	}

	public void commit() {
		getEntityManager().getTransaction().commit();
		getEntityManager().clear();

	}

	public void rollback() {
		getEntityManager().getTransaction().rollback();

	}

	public void flush() {
		getEntityManager().flush();

	}

	/**
	 * 删除情报
	 * 
	 * <pre>
	 * 
	 * EntityManagerHelper.beginTransaction();
	 * AttentionDAO.delete(entity);
	 * EntityManagerHelper.commit();
	 * entity = null;
	 * </pre>
	 * 
	 * @param entity
	 *            操作表对应的实体
	 * @throws RuntimeException
	 *             操作失敗
	 */

	public void delete_NoTransation(T entity, String id) {
		getEntityManager().remove(entity);
	}

	public void delete(T entity, String id) {
		this.delete(entity, id, true);
	}

	public void delete(T entity, String id, boolean autoCommit) {
		log.debug("deleting  instance", Level.INFO, null);
		try {
			if (autoCommit) {
				beginTrans();
			}
			// entity = (T) getEntityManager().getReference(entity.getClass(),
			// id);
			getEntityManager().remove(entity);

			if (autoCommit) {
				commit();
			}
			log.debug("delete successful", Level.INFO, null);
		} catch (RuntimeException re) {
			log.debug("delete failed", Level.SEVERE, re);
			if (autoCommit) {
				rollback();
			}
			throw re;
		}
	}

	/**
	 * 批量删除实体集
	 * 
	 * @param entitys
	 * @return
	 */
	public boolean deleteEntitys(List<T> entitys) {

		// 为了提升性能，我们对数据进行分批次的保存，手动控制更新到数据库
		boolean flag = false;
		try {

			/*
			 * 
			 * // 每100条往数据库里写入一次,相对提升性能，此值可改变 int batchSize = 100; int i = 0;
			 * for (T entity : entitys) { getEntityManager().persist(entity);
			 * i++; if (i % batchSize == 0) { getEntityManager().flush();
			 * getEntityManager().clear(); } }
			 */
			for (T entity : entitys) {
				// entity = (T)
				// getEntityManager().getReference(entity.getClass(), entity);
				getEntityManager().remove(getEntityManager().merge(entity));
			}
			getEntityManager().flush();
			getEntityManager().getTransaction().commit();
			getEntityManager().clear();
			flag = true;
			if (log.isDebugEnabled()) {
				log.debug("批量删除实体成功，" + getEntityManager().getClass().getName());
			}
		} catch (Exception e) {
			flag = false;
			log.error("批量删除实体失败", e);
		}
		return flag;
	}

	public T update(T entity) {
		return getEntityManager().merge(entity);
	}

	/**
	 * 更新
	 * 
	 * <pre>
	 * EntityManagerHelper.beginTransaction();
	 * entity = AttentionDAO.update(entity);
	 * EntityManagerHelper.commit();
	 * </pre>
	 * 
	 * @param entity
	 *            操作表对应的实体
	 * @return Attention 更新结果
	 * @throws RuntimeException
	 *             操作失敗
	 */
	public T update(T entity, boolean autoCommit) {
		log.debug("updating   instance", Level.INFO, null);
		try {
			if (autoCommit) {
				beginTrans();
			}
			T result = getEntityManager().merge(entity);
			if (autoCommit) {
				commit();
			}
			log.debug("update successful", Level.INFO, null);
			return result;
		} catch (RuntimeException re) {
			log.debug("update failed", Level.SEVERE, re);
			if (autoCommit) {
				rollback();
			}
			throw re;
		}
	}

	/**
	 * 根据操作表的属性进行查询
	 * 
	 * @param propertyName
	 *            关注情报表的属性名
	 * @param value
	 *            属性值
	 * @param rowStartIdxAndCount
	 *            Optional int varargs. rowStartIdxAndCount[0] specifies the the
	 *            row index in the query result-set to begin collecting the
	 *            results. rowStartIdxAndCount[1] specifies the the maximum
	 *            number of results to return.
	 * @return List found by query
	 */
	public List<T> findByProperty(String tabname, String propertyName, final Object value,
			final int... rowStartIdxAndCount) {
		log.debug("finding " + tabname + " instance with property: " + propertyName + ", value: " + value, Level.INFO,
				null);
		try {
			final String queryString = "from " + tabname + " model where model." + propertyName + "= "
					+ ":propertyValue";
			Query query = getEntityManager().createQuery(queryString);
			query.setParameter("propertyValue", value);
			if (rowStartIdxAndCount != null && rowStartIdxAndCount.length > 0) {
				int rowStartIdx = Math.max(0, rowStartIdxAndCount[0]);
				if (rowStartIdx > 0) {
					query.setFirstResult(rowStartIdx);
				}

				if (rowStartIdxAndCount.length > 1) {
					int rowCount = Math.max(0, rowStartIdxAndCount[1]);
					if (rowCount > 0) {
						query.setMaxResults(rowCount);
					}
				}
			}
			return query.getResultList();
		} catch (RuntimeException re) {
			log.debug("find by property name failed", Level.SEVERE, re);
			throw re;
		}
	}

	/**
	 * 全表查询
	 * 
	 * @param rowStartIdxAndCount
	 *            Optional int varargs. rowStartIdxAndCount[0] specifies the the
	 *            row index in the query result-set to begin collecting the
	 *            results. rowStartIdxAndCount[1] specifies the the maximum
	 *            count of results to return.
	 * @return List<OfUser> all OfUser entities
	 */
	@SuppressWarnings("rawtypes")
	public List<T> findAll(Class c, String hql, final int... rowStartIdxAndCount) {
		log.debug("finding all  " + c.getName() + " instances", Level.INFO, null);
		try {
			final String queryString = "select model from " + c.getName() + " model where 1=1 " + hql;
			Query query = getEntityManager().createQuery(queryString);
			if (rowStartIdxAndCount != null && rowStartIdxAndCount.length > 0) {
				int rowStartIdx = Math.max(0, rowStartIdxAndCount[0]);
				if (rowStartIdx > 0) {
					query.setFirstResult(rowStartIdx);
				}

				if (rowStartIdxAndCount.length > 1) {
					int rowCount = Math.max(0, rowStartIdxAndCount[1]);
					if (rowCount > 0) {
						query.setMaxResults(rowCount);
					}
				}
			}
			return query.getResultList();
		} catch (RuntimeException re) {
			log.debug("find all failed", Level.SEVERE, re);
			throw re;
		}
	}

	public List<T> findByProperty(String propertyName, Object value, Class<? extends T> c) {
		log.debug("finding " + c.getName() + " instance with property: " + propertyName + ", value: " + value);
		try {

			String queryString = "from " + c.getName() + " as model where model." + propertyName + "= ?";
			Query queryObject = getEntityManager().createQuery(queryString);
			queryObject.setParameter(0, value);
			return queryObject.getResultList();
		} catch (RuntimeException re) {
			log.error("find by property name failed", re);
			throw re;
		}
	}

	/**
	 * 根据主键查询
	 * 
	 * @param c
	 * @param id
	 * @return
	 */
	public T findById(Class<? extends T> c, Long id) {
		log.debug("getting " + c.getName() + " instance with id: " + id);
		try {
			T instance = (T) getEntityManager().find(c, id);
			return instance;
		} catch (RuntimeException re) {
			log.error("get failed", re);
			throw re;
		}
	}

	/**
	 * 查询全部
	 * 
	 * @param c
	 * @return
	 */
	public List<T> findAll(Class<? extends T> c) {
		log.debug("finding all " + c.getName() + " instances");
		try {
			String queryString = "from " + c.getName();
			Query queryObject = getEntityManager().createQuery(queryString);
			return queryObject.getResultList();
		} catch (RuntimeException re) {
			log.error("find all failed", re);
			throw re;
		}
	}

	/**
	 * 解决查询结果集线程安全性问题
	 * 
	 * @param clazz
	 * @param c
	 * @return
	 */
	public static <T> List<T> castList(Class<? extends T> clazz, Collection<?> c) {
		List<T> r = new ArrayList<T>(c.size());
		for (Object o : c)
			r.add(clazz.cast(o));
		return r;
	}
}